/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_kernel.htm
 *
 * Zhiqim Kernel is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.kernel.schedule;

import java.util.ArrayList;
import java.util.List;

import org.zhiqim.kernel.Z;
import org.zhiqim.kernel.config.Group;
import org.zhiqim.kernel.config.Item;
import org.zhiqim.kernel.constants.SignConstants;
import org.zhiqim.kernel.control.ThreadServicer;
import org.zhiqim.kernel.logging.Log;
import org.zhiqim.kernel.logging.LogFactory;
import org.zhiqim.kernel.model.seqs.Sequence;
import org.zhiqim.kernel.util.DateTimes;
import org.zhiqim.kernel.util.Randoms;
import org.zhiqim.kernel.util.Strings;
import org.zhiqim.kernel.util.Threads;

/**
 * 时钟任务器
 *
 * @version v1.0.0 @author zouzhigang 2014-2-27 新建与整理
 */
public class Scheduler extends ThreadServicer implements SignConstants
{
    private static final Log log = LogFactory.getLog(Scheduler.class);
    
    //管理线程
    private ThreadGroup threadGroup;                            //任务线程组
    private String threadName;                                  //任务管理线程名

    //任务列表
    private final Sequence sequence = new Sequence();
    private final List<TaskThreader> taskList = new ArrayList<>();    //定时任务列表
    private final List<Interval> intervalList = new ArrayList<>();    //间隔任务列表

    /*******************************************************************************/
    //创建&销毁
    /*******************************************************************************/
    
    @Override
    protected ThreadGroup getThreadGroup()
    {
        return threadGroup;
    }
    
    @Override
    protected String getThreadName()
    {
        return threadName;
    }
    
    @Override
    protected boolean openBefore()
    {
        String name = Strings.toUpperFirstChar((id == null)?"Scheduler-"+Randoms.letters(4):id);
        threadGroup = new ThreadGroup(name +"-Task-");
        threadName = name;
        return true;
    }
    
    @Override
    protected boolean openAfter()
    {
        if (id == null)
        {//非配置创建
            return true;
        }

        Group grp = Z.conf().group(id);
        if (grp == null || grp.isEmpty())
        {
            return true;
        }
        
        //加载配置的定时任务
        log.info("初始化[定时任务:%s]开始...", id);
        
        for (Item item : grp.list())
        {
            String key = item.getKey();
            String value = item.getString();
            Object task = ScheduleFactory.getTaskThreader(key, value);
            if (task instanceof TaskThreader)
                addTask((TaskThreader)task);
            else if (task instanceof Interval)
                addInterval((Interval)task);
            else
            {
                log.error("初始化[定时任务:%s]配置不正确[%s][%s]", id, key, value);
                return false;
            }
        }
        
        log.info("初始化[定时任务:%s]共[%s]个计划任务和[%s]个轮循任务完成!!!\r\n", id, taskList.size(), intervalList.size());
        return true;
    }
    
    @Override
    protected void closeAfter()
    {
        if (threadGroup != null)
        {//关闭线程组
            try{if (!threadGroup.isDestroyed())threadGroup.destroy();}catch(Throwable e){}
            threadGroup = null;
        }
        
        synchronized (taskList)
        {//关闭所有定时任务
            for (TaskThreader task : taskList)
            {
                task.close();
            }
            taskList.clear();
        }
        
        synchronized (intervalList)
        {//关闭所有循环任务
            for (Interval interval : intervalList)
            {
                interval.close();
            }
            intervalList.clear();
        }
        
        log.info("时钟任务[%s]监视线程退出", threadName);
    }

    /*******************************************************************************/
    //任务&定时器
    /*******************************************************************************/
    
    @SuppressWarnings("unchecked")
    public <T> T getTask(Class<T> cls)
    {
        for (TaskThreader item: taskList)
        {
            if (item.getTask().getClass() == cls)
            {
                return (T)item.getTask();
            }
        }
        
        return null;
    }
    
    /** 增加一个任务，设置任务编号，加载任务参数，放置任务列表 */
    public int addTask(TaskThreader task)
    {
        if (taskList.contains(task))
            return task.getTaskId();
        
        if (!isRunning())
            open();
        
        taskList.add(task);
        
        int taskId = sequence.nextInt();
        task.setScheduleAndTaskId(this, taskId);
        task.open();
        
        log.info("增加计划任务[%s]", task.getThreadName());
        return taskId;
    }
    
    public void removeTask(TaskThreader task)
    {
        if (taskList.remove(task))
            task.close();
    }

    public void removeTask(int taskId)
    {
        TaskThreader qTask = null;
        for (TaskThreader task : taskList)
        {
            if (task.getTaskId() == taskId)
            {
                qTask = task;
                break;
            }
        }
        
        if (qTask != null)
            removeTask(qTask);
    }

    public void addInterval(Interval interval)
    {
        intervalList.add(interval);
        log.info("增加间隔任务[%s]", interval.getThreadName());
    }
    
    public int size()
    {
        return taskList.size();
    }

    public List<TaskThreader> list()
    {
        return taskList;
    }
    
    public int sizeInterval()
    {
        return intervalList.size();
    }
    
    public List<Interval> listInterval()
    {
        return intervalList;
    }
    
    /*******************************************************************************/
    //管理线程运行（每秒给所有任务推送一条消息）
    /*******************************************************************************/

    protected void first()
    {//对时到毫秒000，第一秒不处理
        chkMillis();
    }
    
    /** 每秒给所有任务推送一条消息 */
    protected void loop()
    {
        int[] dts = DateTimes.getCurrent();
        
        int curYearMonth = dts[8];
        int curMonthMaxDay = dts[9];
        int curDay = dts[2];
        int curHour = dts[3];
        int curMinute = dts[4];
        int curSecond = dts[5];
        int curWeek = dts[7];
        
        for (TaskThreader task : taskList)
        {
            task.process(curYearMonth, curMonthMaxDay, curDay, curHour, curMinute, curSecond, curWeek);
        }
        
        int millis = DateTimes.getCurrentMillis();
        Threads.sleepIgnoreException(1000-millis);
    }
    
    /** 对时到毫秒000 */
    private void chkMillis()
    {
        int millis = DateTimes.getCurrentMillis();
        if (millis > 0)
            Threads.sleepIgnoreException(1000-millis);
    }
    
    /********************************************************************************/
    //提交任务的定时安排
    /********************************************************************************/
    
    /**
     * 创建Interval，支持传入任务，每次等待时间，启动立即运行，默认一直运行
     * 
     * @param task              任务
     * @param millis            每次等待时间毫秒数
     */
    public void addIntervalTask(Task task, int millis)
    {
        addInterval(Interval.shedule(task, millis));
    }
    
    /**
     * 创建Interval，支持传入任务，时间参数，默认一直运行
     * 
     * @param task              任务
     * @param firstMillis       第一次等待时间毫秒数
     * @param middleMillis      每次等待时间毫秒数
     */
    public void addIntervalTask(Task task, int firstMillis, int middleMillis)
    {
        addInterval(Interval.shedule(task, firstMillis, middleMillis));
    }
    
    /**
     * 创建Interval，支持传入任务，时间参数和次数
     * 
     * @param task              任务
     * @param firstMillis       第一次等待时间毫秒数
     * @param middleMillis      每次等待时间毫秒数
     * @param times             执行次数
     */
    public void addIntervalTask(Task task, int firstMillis, int middleMillis, int times)
    {
        addInterval(Interval.shedule(task, firstMillis, middleMillis, times));
    }
}
